package com.hmall.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.common.dto.FilterDTO;
import com.hmall.common.feign.ItemClient;
import com.hmall.common.pojo.Item;
import com.hmall.common.pojo.ItemDoc;
import com.hmall.common.vo.PageVo;
import com.hmall.mapper.EsMapper;
import com.hmall.service.EsService;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author leslie
 * @create 2023/9/1 0001 17:27
 */
@Service
public class EsServiceImpl implements EsService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    ItemClient itemClient;
    @Autowired
    EsMapper esMapper;


    @Override
    public void insertBulkData() {

        Long i = 1l;
        while (true) {
            BulkRequest request = new BulkRequest();
            PageVo<Item> itemPageVo = itemClient.pageQuery(i, 15000l);
            List<Item> list = itemPageVo.getList();
            if (list != null && list.size() != 0) {
                for (Item item : list) {
                    ItemDoc itemDoc = new ItemDoc(item);
                    // 3.把新增文档请求对象封装到批量请求对象中
                    request.add(new IndexRequest("item") // 2.构建新增文档请求对象
                            // 一定要指定id，保证幂等性(不管调用多少次查询接口，结果都是一样的)
                            .id(String.valueOf(itemDoc.getId()))
                            .source(JSON.toJSONString(itemDoc), XContentType.JSON));
                }
                // 4.发送请求
                try {
                    BulkResponse response = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
                    i++;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                break;
            }

        }

    }

    @Override
    public List<String> autoCompletion(String key) {

        // 1.创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("item");
        // 2.封装DSL -- (搜索条件对象)
        SearchSourceBuilder searchSourceBuilder = searchRequest.source();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchSourceBuilder.from(0).size(0);
        searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion("suggestKeyWord", SuggestBuilders
                .completionSuggestion("suggestion")
                .prefix(key)
                .skipDuplicates(true)
                .size(10)));
        // 3.执行查询
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 4.解析结果 -- 自动补全数据
        Suggest suggest = response.getSuggest();
        if (suggest == null) {
            return null;
        }
        List<String> suggestList = new ArrayList<>();
        Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> suggestKeyWord = suggest.getSuggestion("suggestKeyWord");
        if (suggestKeyWord == null) {
            return null;
        }
        for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> options : suggestKeyWord) {
            List<? extends Suggest.Suggestion.Entry.Option> options1 = options.getOptions();
            for (Suggest.Suggestion.Entry.Option option : options1) {
                String s = option.getText().toString();
                suggestList.add(s);
            }
        }
        return suggestList;
    }

    @Override
    public Map<String, List<String>> filterSomething(FilterDTO filterDTO) {
        SearchRequest searchRequest = new SearchRequest("item");
        SearchSourceBuilder sourceBuilder = searchRequest.source();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (filterDTO.getKey() != null && filterDTO.getKey() != "") {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", filterDTO.getKey()));
            filterDTO.setCategory(filterDTO.getKey());
        } else {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        if (filterDTO.getCategory() != null && filterDTO.getCategory() != "") {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", filterDTO.getCategory()));
        }
        if (filterDTO.getBrand() != null && filterDTO.getBrand() != "") {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", filterDTO.getBrand()));
        }
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(filterDTO.getMinPrice()).lte(filterDTO.getMaxPrice()));
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.from(2).size(20);
        sourceBuilder.sort("price", SortOrder.DESC);
        sourceBuilder.aggregation(AggregationBuilders.terms("brandList").field("brand").size(100));
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryList").field("category").size(100));
        Map<String, List<String>> filterMap = new HashMap<>();
        List<String> brandLists = new ArrayList<>();
        List<String> categoryLists = new ArrayList<>();
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = response.getAggregations();
            if (aggregations != null) {
                Terms brandList = aggregations.get("brandList");
                Terms categoryList = aggregations.get("categoryList");
                List<? extends Terms.Bucket> brandBuckets = brandList.getBuckets();
                for (Terms.Bucket bucket : brandBuckets) {
                    brandLists.add(bucket.getKey().toString());
                }
                List<? extends Terms.Bucket> categoryBuckets = categoryList.getBuckets();
                for (Terms.Bucket categoryBucket : categoryBuckets) {
                    categoryLists.add(categoryBucket.getKey().toString());
                }
                filterMap.put("category", categoryLists);
                filterMap.put("brand", brandLists);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return filterMap;
    }

    @Override
    public PageVo list(FilterDTO filterDTO) {
        SearchRequest searchRequest = new SearchRequest("item");
        SearchSourceBuilder sourceBuilder = searchRequest.source();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (filterDTO.getKey() != null && !filterDTO.getKey().equals("")) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", filterDTO.getKey()));
        } else {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        if (filterDTO.getBrand() != null && !filterDTO.getBrand().equals("")) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", filterDTO.getBrand()));
        }
        if (filterDTO.getCategory() != null && !filterDTO.getCategory().equals("")) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", filterDTO.getCategory()));
        }
        FunctionScoreQueryBuilder isAD = QueryBuilders.functionScoreQuery(
                // 原始查询，相关性算分的查询
                boolQueryBuilder,
                // function score的数组
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        // 其中的一个function score 元素
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                // 过滤条件
                                QueryBuilders.termQuery("isAD", true),
                                // 算分函数
                                ScoreFunctionBuilders.weightFactorFunction(100)
                        )
                });
        sourceBuilder.query(isAD);
        sourceBuilder.from(Math.toIntExact(filterDTO.getPage())).size(Math.toIntExact(filterDTO.getSize()));
        sourceBuilder.trackTotalHits(true);



        sourceBuilder.highlighter(new HighlightBuilder().field("name").preTags("<em>").postTags("</em>").requireFieldMatch(true));
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();
            SearchHit[] searchHits = hits.getHits();
            long total = hits.getTotalHits().value;
            if (searchHits == null || searchHits.length == 0) {
                return null;
            }
            List<ItemDoc> itemDocs = new ArrayList<>();
            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                Item item = JSON.parseObject(sourceAsString, Item.class);
                item.setPrice(item.getPrice() * 100);
                ItemDoc itemDoc = new ItemDoc(item);
                HighlightField name = searchHit.getHighlightFields().get("name");
                if (name != null) {
                    String brandHighlighter = name.getFragments()[0].toString();
                    if (StringUtils.isNotBlank(brandHighlighter)) {
                        itemDoc.setName(brandHighlighter);
                    }
                }
                itemDocs.add(itemDoc);
            }
            return new PageVo(total, itemDocs);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 更新Es
     *
     * @param id
     */
    public void insertToEsById(Long id) {
        Item item = esMapper.selectById(id);
        if (item == null) {
            return;
        }
        ItemDoc itemDoc = new ItemDoc(item);
        IndexRequest indexRequest = new IndexRequest("item").id(itemDoc.getId().toString()).source(JSON.toJSONString(itemDoc), XContentType.JSON);
        try {
            restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除Es数据
     *
     * @param id
     */

    @Override
    public void deleteEsById(Long id) {
        DeleteRequest request = new DeleteRequest("item", id.toString());
        try {
            restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
